package com.atguigu.yygh.order.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.atguigu.yygh.enums.OrderStatusEnum;
import com.atguigu.yygh.enums.PaymentStatusEnum;
import com.atguigu.yygh.hosp.globalexp.YyghException;
import com.atguigu.yygh.hosp.util.HttpRequestHelper;
import com.atguigu.yygh.model.order.OrderInfo;
import com.atguigu.yygh.model.order.PaymentInfo;
import com.atguigu.yygh.model.user.Patient;
import com.atguigu.yygh.order.client.HospFeignClient;
import com.atguigu.yygh.order.mapper.OrderInfoMapper;
import com.atguigu.yygh.order.mapper.PaymentInfoMapper;
import com.atguigu.yygh.order.service.OrderInfoService;
import com.atguigu.yygh.order.service.PaymentInfoService;
import com.atguigu.yygh.order.service.WeixinService;
import com.atguigu.yygh.rabbit.consts.MqConst;
import com.atguigu.yygh.rabbit.service.RabbitService;
import com.atguigu.yygh.user.client.PatientFeignClient;
import com.atguigu.yygh.vo.hosp.ScheduleOrderVo;
import com.atguigu.yygh.vo.msm.MsmVo;
import com.atguigu.yygh.vo.order.OrderCountQueryVo;
import com.atguigu.yygh.vo.order.OrderCountVo;
import com.atguigu.yygh.vo.order.OrderMqVo;
import com.atguigu.yygh.vo.order.OrderQueryVo;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.joda.time.DateTime;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.*;
import java.util.stream.Collectors;

@Service
public class OrderInfoServiceImpl extends ServiceImpl<OrderInfoMapper, OrderInfo> implements OrderInfoService {

    @Autowired
    PatientFeignClient patientFeignClient;
    @Autowired
    HospFeignClient hospFeignClient;

    @Autowired
    RabbitService rabbitService;
    @Autowired
    PaymentInfoMapper paymentInfoMapper;
    @Autowired
    WeixinService weixinService;
    /**
     * 提交订单
     *   (1) 准备数据
     *   (2) 调用医院端"预约下单"
     */
    @Override
    public Long submitOrder(Long patientId, String scheduleId,String hoscode) {
        Patient patient = patientFeignClient.getPatient(patientId);
        //可以减少一次远程服务调用
        String userPhone = (String) patient.getParam().get("userPhone");

        ScheduleOrderVo scheduleOrderVo = hospFeignClient.getScheduleOrderVo(scheduleId);
        //-----------------------------------------------------------------

        if(!hoscode.equals(scheduleOrderVo.getHoscode())){
            throw new YyghException(20001,"该排班和该医院不匹配,订单提交失败");
        }

        //-----------------------------------------------------------------

        String apiUrl = hospFeignClient.getApiUrl(scheduleOrderVo.getHoscode()); //第三次使用hoscode(查询该医院的接口地址)

        //根据就诊人的id查询就诊人对应的用户的手机号(远程服务调用)
      //  String userPhone=patientFeignClient.getUserPhone(patientId);



        //1.调用医院端"预约下单"
        String url=apiUrl+"/order/submitOrder";

        Map<String,Object> paramMap = new HashMap<>();
        paramMap.put("hoscode",scheduleOrderVo.getHoscode()); //调用医院端接口时第一次使用hoscode
        paramMap.put("depcode",scheduleOrderVo.getDepcode());
        paramMap.put("hosScheduleId",scheduleOrderVo.getHosScheduleId());
        paramMap.put("reserveDate",scheduleOrderVo.getReserveDate());
        paramMap.put("reserveTime",scheduleOrderVo.getReserveTime());
        paramMap.put("amount",scheduleOrderVo.getAmount());

        paramMap.put("name",patient.getName());
        paramMap.put("sex",patient.getSex());
        paramMap.put("birthdate",patient.getBirthdate());
        paramMap.put("phone",patient.getPhone());
        paramMap.put("isMarry",patient.getIsMarry());
        paramMap.put("provinceCode",patient.getProvinceCode());
        paramMap.put("cityCode",patient.getCityCode());
        paramMap.put("districtCode",patient.getDistrictCode());
        paramMap.put("address",patient.getAddress());
        paramMap.put("contactsName",patient.getContactsName());
        paramMap.put("contactsCertificatesType",patient.getContactsCertificatesType());
        paramMap.put("contactsCertificatesNo",patient.getContactsCertificatesNo());
        paramMap.put("contactsPhone",patient.getContactsPhone());
        paramMap.put("isInsure",patient.getIsInsure());

/*    jsonObject的格式
      json如果想要获取某个参数的话,String类型就是getString
      Integer就是getInteger
      Json就是getJSON
        {
            code
            message
            data:{
                   hosRecordId
                   number
                   reservedNumber
                   availableNumber
                   fetchTime
                   fetchAddress  }
        }

 */
        JSONObject jsonObject = HttpRequestHelper.sendRequest(paramMap, url);//这行代码直接到医院端模拟系统
        Integer code = jsonObject.getInteger("code");
        if(code!=200){
            throw new YyghException(20001,"医院端预约接口调用失败");
        }


        JSONObject data = jsonObject.getJSONObject("data");

        Long hosRecordId = data.getLong("hosRecordId");//医院端订单主键
        Integer number = data.getInteger("number");    //预约序号

        //医院端返回的该排班最新的号源数量(将来要把两个num更新到Mongodb中)
        Integer reservedNumber = data.getInteger("reservedNumber");
        Integer availableNumber = data.getInteger("availableNumber");
        String fetchTime = data.getString("fetchTime");
        String fetchAddress = data.getString("fetchAddress");

        //2.创建平台端的订单
        //2、创建平台端的订单
        OrderInfo orderInfo = new OrderInfo();
        orderInfo.setUserId(patient.getUserId());//用户id（也可以从请求头中解析令牌，获取到用户id）
        String outTradeNo = System.currentTimeMillis() + "" + new Random().nextInt(100);
        orderInfo.setOutTradeNo(outTradeNo);//订单编号，保证唯一
        orderInfo.setHoscode(scheduleOrderVo.getHoscode());
        orderInfo.setHosname(scheduleOrderVo.getHosname());
        orderInfo.setDepcode(scheduleOrderVo.getDepcode());
        orderInfo.setDepname(scheduleOrderVo.getDepname());
        orderInfo.setTitle(scheduleOrderVo.getTitle());
        orderInfo.setScheduleId(scheduleId);//排班的id（平台端的排班id）
        orderInfo.setReserveDate(scheduleOrderVo.getReserveDate());
        orderInfo.setReserveTime(scheduleOrderVo.getReserveTime());

        orderInfo.setPatientId(patient.getId());
        orderInfo.setPatientName(patient.getName());
        orderInfo.setPatientPhone(patient.getPhone());

        //医院端会返回的业务数据
        orderInfo.setHosRecordId(String.valueOf(hosRecordId));//医院端创建的订单的id
        orderInfo.setNumber(number);//医院端返回的预约序号
        orderInfo.setFetchAddress(fetchAddress);//取号地点
        orderInfo.setFetchTime(fetchTime);//取号时间

        orderInfo.setAmount(scheduleOrderVo.getAmount());
        orderInfo.setQuitTime(scheduleOrderVo.getQuitTime());//截止退号时间
        orderInfo.setOrderStatus(OrderStatusEnum.UNPAID.getStatus());//订单的状态，0-预约成功未支付


        //创建平台端的订单
        baseMapper.insert(orderInfo);
//--------------------------------------------------------------------------------
        //封装mq中的数据(发布到交换机上)
        //rabbitmq ,订单服务中封装消息对象
        OrderMqVo orderMqVo = new OrderMqVo();
        //这三个属性是医院服务使用的
        orderMqVo.setScheduleId(scheduleId);  //Mongodb中排班的id
        orderMqVo.setAvailableNumber(availableNumber);//医院返回的两个最新的num
        orderMqVo.setReservedNumber(reservedNumber);
        //msmvo最终给短信服务使用(手机号+短信)
        MsmVo msmVo = new MsmVo();
       // msmVo.setPhone(patient.getPhone());  //就诊人的手机号
        msmVo.setPhone(userPhone);//用户的手机号
        msmVo.setMessage("【尚医通】订单已创建成功,请及时支付!");
        orderMqVo.setMsmVo(msmVo);

        //向第一个交换机上投递消息
        //交换机的名称,交换机的路由键,交换机封装的消息orderMqVo,设置了消息转换器,可以自定义转换消息的对象
        rabbitService.sendMessage(MqConst.ORDER_EXCHANGE,MqConst.ORDER_KEY,orderMqVo);
        return orderInfo.getId();//返回平台端订单的id（下一个页面显示平台端的订单详情）
    }



    @Override
    public OrderInfo getOrder(Long orderId) {
        OrderInfo orderInfo = baseMapper.selectById(orderId);
        this.packOrderInfo(orderInfo);
        return orderInfo;
    }

    @Override
    public Long checkExistOrder(Long patientId, String scheduleId) {
        QueryWrapper<OrderInfo> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("patient_id",patientId);
        queryWrapper.eq("schedule_id",scheduleId);

        //o1=-1
        //o2=0
        //最多只能有一个不等于-1的订单

        //Integer n = baseMapper.selectCount(queryWrapper);
        //该订单的状态有可能是被取消的
        //OrderInfo orderInfo = baseMapper.selectOne(queryWrapper);
        List<OrderInfo> list = baseMapper.selectList(queryWrapper);
        for (OrderInfo orderInfo : list) {
            Integer orderStatus = orderInfo.getOrderStatus();
            if(orderStatus!=-1){
                //存在一个未取消的订单,不允许重复挂号(前端会跳转到该订单详情)
                return orderInfo.getId();
            }
        }
        //订单存在,并且=-1是未被取消
//        if(orderInfo!=null && orderInfo.getOrderStatus()!=-1){
//            return orderInfo.getId(); //不可以重复挂号
//        }
        return null;

    }

    @Override
    public Page<OrderInfo> selectPage(Long pageNum, Long pageSize, OrderQueryVo orderQueryVo) {
        String orderStatus = orderQueryVo.getOrderStatus();
        Long patientId = orderQueryVo.getPatientId();
        Long userId = orderQueryVo.getUserId();
        QueryWrapper<OrderInfo> queryWrapper = new QueryWrapper<>();
        if(!StringUtils.isEmpty(orderStatus)){
            queryWrapper.eq("order_status",orderStatus);
        }
        if(patientId!=null){
            queryWrapper.eq("patient_id",patientId);
        }
        if(userId!=null){
            queryWrapper.eq("user_id",userId);
        }
        Page<OrderInfo> orderInfoPage=new Page<>(pageNum,pageSize);
        baseMapper.selectPage(orderInfoPage,queryWrapper);
        orderInfoPage.getRecords().forEach(orderInfo -> {
            this.packOrderInfo(orderInfo);
        });
        return orderInfoPage;
    }

    @Override
    public void paySuccess(Long orderId,Map<String,String> map) {
        OrderInfo orderInfo = baseMapper.selectById(orderId);
        String hoscode = orderInfo.getHoscode();
        String apiUrl = hospFeignClient.getApiUrl(hoscode);

        //1.调用医院端的"更新支付状态"接口
        String url=apiUrl+"/order/updatePayStatus";
        Map<String,Object> paramMap=new HashMap<>();
        paramMap.put("hoscode",orderInfo.getHoscode());
        paramMap.put("hosRecordId",orderInfo.getHosRecordId());  //医院端订单的主键

        //调用医院端的各个接口用的是工具类 HttpRequestHelper
        JSONObject jsonObject = HttpRequestHelper.sendRequest(paramMap, url);
        Integer code = jsonObject.getInteger("code");
        if(code!=200){
            throw new YyghException(20001,"医院端更新支付状态接口调用失败");
        }

        //2.平台端的支付状态改成已支付
        orderInfo.setOrderStatus(OrderStatusEnum.PAID.getStatus());  //改成已支付
        baseMapper.updateById(orderInfo);

        //3.支付记录状态改成已支付(1变2)
        QueryWrapper<PaymentInfo> wrapper = new QueryWrapper<>();
        wrapper.eq("order_id",orderInfo.getId());
        PaymentInfo paymentInfo = paymentInfoMapper.selectOne(wrapper);

        paymentInfo.setPaymentStatus(PaymentStatusEnum.PAID.getStatus());
        paymentInfo.setCallbackTime(new Date()); //当前支付的时间
        paymentInfo.setTradeNo(map.get("transaction_id")+ ""); //支付成功后,微信端返回的transaction_id(微信支付订单号/支付流水号)
        paymentInfo.setCallbackContent(map.toString()); //map转成字符串,备份到该字段
        paymentInfoMapper.updateById(paymentInfo);
    }

    //点击取消按钮的时候调用
    @Override
    public void cancelOrder(Long orderId) {
        //1.判断订单是否能够取消
        OrderInfo orderInfo = baseMapper.selectById(orderId);
        Date quitTime = orderInfo.getQuitTime();
        DateTime dateTime = new DateTime(quitTime);
        if(dateTime.isBeforeNow()){
            throw new YyghException(20001,"退号截止时间已过,暂不能取消!");
        }

        //2.调用医院端"取消预约"接口,医院端:修改医院状态改成-1,排班的availableNumber+1
        String apiUrl = hospFeignClient.getApiUrl(orderInfo.getHoscode());
        String url=apiUrl+"/order/updateCancelStatus";

        Map<String,Object> paramMap=new HashMap<>();
        paramMap.put("hoscode",orderInfo.getHoscode());
        paramMap.put("hosRecordId",orderInfo.getHosRecordId());

        JSONObject jsonObject = HttpRequestHelper.sendRequest(paramMap, url);
        Integer code = jsonObject.getInteger("code");
        if(code!=200){
            throw new YyghException(20001,"医院端取消预约接口调用失败");
        }

        //3.判断平台端订单的状态如果=1(已支付),执行退款流程
        Integer orderStatus = orderInfo.getOrderStatus();
        if(OrderStatusEnum.PAID.getStatus()==orderStatus){
            weixinService.refund(orderId);
        }

        //4.将平台端的订单状态改成已取消(记录取消时间)
        orderInfo.setOrderStatus(OrderStatusEnum.CANCLE.getStatus());  //-1
        orderInfo.setCancelDate(new Date());
        baseMapper.updateById(orderInfo);


        //5.异步将mongdb中排班的availableNumber+1,异步给用户发送短信通知
        OrderMqVo orderMqVo = new OrderMqVo();
        orderMqVo.setScheduleId(orderInfo.getScheduleId());  //mongdb中的排班的id(平台端的订单中有排班的id)
       // orderMqVo.setReservedNumber(); 取消预约时,封装的消息对象中的num不需要赋值
       // orderMqVo.setAvailableNumber();

        MsmVo msmVo = new MsmVo();
        msmVo.setPhone(patientFeignClient.getUserPhone(orderInfo.getPatientId())); //用户的手机号
      //  msmVo.setPhone(orderInfo.getPatientPhone()); //就诊人的手机号
        msmVo.setMessage("尚医通订单已取消!");
        orderMqVo.setMsmVo(msmVo);

        //向第三个队列发送消息,第三个服务由医院端进行监听
        rabbitService.sendMessage(MqConst.CANCEL_ORDER_EXCHANGE,MqConst.CANCEL_ORDER_KEY,orderMqVo);

    }

    @Override
    public Integer checkCancelOrderCount(Long patientId, String scheduleId) {
        //这个就诊人在这个排班下的订单在今天的取消次数
        QueryWrapper<OrderInfo> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("patient_id",patientId);
        queryWrapper.eq("schedule_id",scheduleId);
        queryWrapper.eq("order_status",-1);
        queryWrapper.eq("cancel_date",new DateTime().toString("yyyy-MM-dd"));
        return baseMapper.selectCount(queryWrapper);
    }

    @Override
    public Map getOrderCount(OrderCountQueryVo orderCountQueryVo) {
//SELECT hosname,reserve_date,COUNT(1) AS ordercount FROM `order_info` GROUP BY reserve_date,hosname
        List<OrderCountVo> list = baseMapper.getOrderCount(orderCountQueryVo);

//        List<String> dateList = new ArrayList<>();
//        List<Integer> countList = new ArrayList<>();
//        for (OrderCountVo orderCountVo : list) {
//            dateList.add(orderCountVo.getReserveDate());
//            countList.add(orderCountVo.getCount());
//        }

        //java8中的流的写法
        //collect(Collectors.toList())
        // 的作用就是将Stream中的元素收集到一个List集合中，方便后续的处理和使用。
        //Collectors.toList()方法会创建一个新的ArrayList实例，
        // 并将Stream中的元素逐个添加到该集合中。
        List<String> dateList = list.stream().map(OrderCountVo::getReserveDate).collect(Collectors.toList());
        List<Integer> countList = list.stream().map(OrderCountVo::getCount).collect(Collectors.toList());

        Map map=new HashMap();
        map.put("dateList",dateList);
        map.put("countList",countList);
        return map;
    }

    private void packOrderInfo(OrderInfo orderInfo) {
        Integer orderStatus = orderInfo.getOrderStatus();
        String statusNameByStatus = OrderStatusEnum.getStatusNameByStatus(orderStatus);
        orderInfo.getParam().put("orderStatusString",statusNameByStatus);   //订单状态字符串
    }
}
